package com.googlecode.imule.discovery;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

import com.apple.dnssd.DNSRecord;
import com.apple.dnssd.DNSSD;
import com.apple.dnssd.DNSSDException;
import com.apple.dnssd.DNSSDRegistration;
import com.apple.dnssd.DNSSDService;
import com.apple.dnssd.RegisterListener;
import com.apple.dnssd.TXTRecord;

/**
 * A discovery service using Bonjour. The discover service is in control of the 
 * registration and discovery of Bonjour services.
 * 
 * @author Graham Williamson, Neil Cowzer
 */

public class DiscoveryServiceX implements RegisterListener{

	/* The service name and type string. */
	private String serviceRegistrationString;

	/* The name of this application. */
	private String name;

	/* The service browser that searches for new and lost services. */
	private DNSSDService browser;

	/* The object that keeps the register information regarding this service. */
	private DNSSDRegistration register;

	/* The list of available contacts. */
	private ContactList contacts;

	/* The DNSRecord is used. */
	private DNSRecord dnsRecord; 
	
	private DatagramSocket outSocket;
	private DatagramPacket packet;

	/**
	 * Initialise and register a application with a given name if possible. 
	 * 
	 * @param name The preferred name for this client
	 * @param inputSocket
	 * @throws DNSSDException  if there are complications registering the service
	 */
	public DiscoveryServiceX(String name, DatagramSocket inSocket) throws DNSSDException, SocketException {
		super();

		this.name = name;
		this.serviceRegistrationString = "_imule._udp";
		
		//DatagramSocket inSocket = new DatagramSocket();
		System.out.println(inSocket.getLocalPort());
		packet = new DatagramPacket(new byte[4096], 4096);
		outSocket = new DatagramSocket();

		contacts = new ContactList();

		register = DNSSD.register(name, serviceRegistrationString, inSocket.getLocalPort(), this);
		browser = DNSSD.browse(serviceRegistrationString, new BrowserAdaptor(contacts));
	}
	
	public String getName(){
		return name;
	}

	/**
	 * Something has gone wrong, the service has failed to register.
	 * 
	 * (Do not call this method directly!)
	 * 
	 * @see com.apple.dnssd.BaseListener#operationFailed(com.apple.dnssd.DNSSDService, int)
	 */
	public void operationFailed(DNSSDService service, int errorCode) {
		System.out.println("Browsing error with service " + service.toString() + ". Error code: " + errorCode);
		System.exit(1);	// close indicating an error occurred
	}

	/**
	 * Called when a service has registered. Upon successful registration 'update' the service's name.
	 * 
	 * (Do not call this method directly!)
	 * 
	 * @see com.apple.dnssd.RegisterListener#serviceRegistered(com.apple.dnssd.DNSSDRegistration, int, java.lang.String, java.lang.String, java.lang.String)
	 */
	public void serviceRegistered(DNSSDRegistration registration, int flags,
			String serviceName, String regType, String domain) {
		// ensure that the name and registered name are the same (clashes force renames)
		name = serviceName;
	}


	/**
	 * Set a text status of the service.
	 * 
	 *	TODO: possibly refactor to use RDF
	 * 
	 * @param status the status of the service
	 */
	public void setStatus(String status){
		TXTRecord record = new TXTRecord();
		record.set("status", status);

		try {
			if (dnsRecord == null)
				dnsRecord = register.getTXTRecord();

			dnsRecord.update(0, record.getRawBytes(), 0);
		} catch (DNSSDException e) {
			System.out.println("update failed");
		}
	}
	
	public void send(String message, String serviceName){
		Contact contact = contacts.getContact(serviceName);
		try {
			send(message, contact.getIpAddress(), contact.getPort());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void broadcast(String message) {
		for (Contact contact : contacts) {
			try {
				send(message, contact.getIpAddress(), contact.getPort());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void send(String message, InetAddress ip, int port) throws IOException{
		byte[] data = message.getBytes("ISO-8859-1");
		DatagramPacket packet = new DatagramPacket(data, data.length);
		
		packet.setAddress(ip);
		packet.setPort(port);
		outSocket.send(packet);
	}
	
	
	public ContactList getContacts() {
		return contacts;
	}

	public void print(String message){
		System.out.println(message);
	}

	/**
	 *	Shutdown the application correctly.
	 */
	public void close(){
		register.stop();	// removes register object for this node
		browser.stop();	// stop searching/browsing the network
	}
	
/*	public static void main(String[] args) throws SocketException, DNSSDException {
		DiscoveryServiceX temp = new DiscoveryServiceX("test "+ Math.random());
		
		Scanner scanner = new Scanner(System.in);
		String input = "";

		while(!input.equalsIgnoreCase("close")){
			input = scanner.nextLine();
			temp.broadcast(input);
		}
		System.out.println("closing application");
		System.exit(0);
		
	}*/
}
